package main;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * @author Matt
 *
 */
public class Score extends JPanel implements MouseListener {

	public static final String[] NOTES = {"a", "a+/b-", "b", "c", "c+/d-", "d", "d+/e-",
		"e", "f", "f+/g-", "g", "g+/a-"};

	public static final int BAR_LINE_SPACING = 10;
	public static final int BAR_LINE_WIDTH = 3;
	public static final int TOTAL_STAFF_SIZE = 5*BAR_LINE_WIDTH + 4*BAR_LINE_SPACING;
	public static int BAR_LINE_LENGTH;

	public static final int NOTE_HEAD_SIZE = BAR_LINE_SPACING+BAR_LINE_WIDTH;
	public static final int NOTE_STEM_LENGTH = 45;
	public static final int NOTE_STEM_WIDTH = 3;
	public static final int OCTAVE_SPACING = BAR_LINE_SPACING*4 + BAR_LINE_WIDTH*5-3;


	public final Dimension GUI_SIZE;

	public static final int STAFF_OFFSET_X = 10;
	public static final int STAFF_OFFSET_Y = 30;

	public static int BASE_OCTAVE = 4;
	public static double FRACTIONAL_BPMEASURE;
	public static final int MEASURE_LINE_WIDTH = NOTE_STEM_WIDTH;


	private KeySignature keySignature;
	private int numStaves = 4;
	private Clef clef;
	private TimeSignature timeSignature;
	private static ArrayList<Integer> noteSpacing = new ArrayList<Integer>();
	private ArrayList<Note> notes = new ArrayList<Note>();
	private static String[] noteOrder = new String[9];
	private String name;
	private ScoreImage image;
	private String scoreCode;

	/**
	 * 
	 * @param n name
	 * @param d dimensions
	 * @param c clef
	 * @param key key signature
	 * @param m major or minor
	 * @param t time signature
	 */
	public Score(String n, Dimension d, String c, String key,  boolean m, TimeSignature t){
		super();
		GUI_SIZE = d;
		BAR_LINE_LENGTH = (int)GUI_SIZE.width-50;
		name = n;
		setSize(GUI_SIZE);
		generateNoteSpacing();
		clef = new Clef(this, c);
		if (!clef.isTreble()) BASE_OCTAVE = 2;
		timeSignature = t;
		FRACTIONAL_BPMEASURE = t.getBpm()/t.getOneBeat();
		generateNoteOrder();
		keySignature = new KeySignature(key, clef, this, m);
		image = new ScoreImage(getWidth(), getHeight(), this);
		scoreCode = this.toString() + "\n";
		//		for(int i = 0; i < noteOrder.length; i++)
		//			System.out.print(noteOrder[i] + " ");
		addMouseListener(this);

	}


	/**
	 * Generates the spacing array for the notes from the top to the bottom
	 */
	private void generateNoteSpacing() {
		int offset = -NOTE_HEAD_SIZE/2;
		noteSpacing.add(offset);
		for(int i = 1; i < 9; i++ ){
			noteSpacing.add(offset+i*NOTE_HEAD_SIZE/2);
		}
	}

	/**
	 * Generates the order of the notes depending on clef from bottom line to top line
	 */
	private void generateNoteOrder(){ 
		int oct = BASE_OCTAVE;
		char[] alphabet = "abcdefg".toCharArray();
		char start = clef.isTreble()? Clef.TREBLE_CLEF_BOTTOM_LINE: Clef.BASS_CLEF_BOTTOM_LINE;
		int startIndex = 0;
		for(int i = 0; i < alphabet.length; i++){
			if (start == alphabet[i])
				startIndex = i;
		}
		noteOrder[0] = start + "" + oct;
		int k = startIndex+1;
		for (int i=1; i < 9; i++){
			k%=alphabet.length;
			if(alphabet[k] == 'c') oct++;
			noteOrder[i] = alphabet[k]+""+oct;
			k++;
		} //creates a matrix with the order of the notes
	}


	/**
	 * Given a String of note codes is breaks them up and makes the notes
	 * @param n String of codes
	 */
	public void interpret(String n){
		ArrayList<Note> currentNoteString = new ArrayList<Note>();
		String nextNote;
		n.trim();
		do{
			int nextSpace = n.indexOf(' ');
			if (nextSpace != -1){
				nextNote = n.substring(0, nextSpace+1);
				n = n.substring(nextSpace+1);
			} //Cuts the next code from the string 
			else{
				nextNote = n;
				n = "";
			}
			nextNote.trim();
			n.trim();
			Note newNote = new Note(nextNote, this);
			if(!newNote.isValid()){
				if(newNote.isEmpty()) return;
				showInvalidNoteErrorMessage(newNote.getValue() + "");
				continue;
			}
			notes.add(newNote);
			scoreCode += nextNote;
			currentNoteString.add(newNote);
		}while(n.indexOf(' ') > 0 || (n.indexOf(' ') < 0 && n.length() > 0));
		image.paintNotes(currentNoteString);
		repaint();
		scoreCode += " ";
	}
	
	/**
	 * Error message when the note isnt valid
	 * @param string wrong note
	 */
	private void showInvalidNoteErrorMessage(String string) {
		JOptionPane.showMessageDialog(this, string +  " is not a valid note", "Invalid note", JOptionPane.ERROR_MESSAGE);
		
	}

	/**
	 * Error message when the note is too long
	 * @param string wrong note 
	 */
	public void showTooLongDurationErrorMessage() {
		JOptionPane.showMessageDialog(this, "Note is too long", "Invalid note", JOptionPane.ERROR_MESSAGE);
		
	}



	/**
	 * redraw the screen
	 */
	public void redraw(){
		update(image.getGraphics());
		repaint();
	}

	/**
	 * Checks to see if a point is contained by any note
	 * @param p
	 */
	public void checkPoint(Point p){
		for(Note n: notes){
			if (n.contains(p)){
				System.out.println("contains");
				if(n.isHighlighted())
					n.edit();
				else n.highlight();
			}else n.unhighlight();
		}
	}

	/**
	 * paints the score
	 */
	public void paint(Graphics graphics){
		graphics.drawImage(image, 0, 0, null);
	}



	/**
	 * 
	 * @return key signature
	 */
	public KeySignature getKeySignature(){
		return keySignature;
	}

	/**
	 * 
	 * @return spacing lists for the notes
	 */
	public ArrayList<Integer> getSpacingList(){
		return noteSpacing;
	}

	/**
	 * 
	 * @param note
	 * @return vertical offset of the given note 
	 */
	public static int getPosition(Note note){
		int position = 0;
		int givenOctave = note.getOctave();
		char n = note.getValue();
		for(int i = noteOrder.length-1; i >=0; i--)
			if(n == noteOrder[i].charAt(0)){ 
				int oct = Integer.parseInt(noteOrder[i].charAt(1)+ "");
				int diff = oct-givenOctave;
				position = noteSpacing.get(8-i);
				if (diff != 0){
					position += diff*OCTAVE_SPACING;
					if(diff > 0) position-=NOTE_HEAD_SIZE/2;
					else position += NOTE_HEAD_SIZE/2;
					note.setOffStaff(true);
				}
				return position;
			}
		return position;
	}

	/**
	 * called from key signature to figure out how to draw the key signature
	 * @param n
	 * @return index of the position for the note
	 */
	public static int getPosition(char n){
		int ret = 0;
		for(int i = 0; i < noteOrder.length; i++)
			if(n == noteOrder[i].charAt(0))
				ret = i;
		return ret;
	}

	/**
	 * @return "name in x Major/minor"
	 */
	public String toString(){
		String s = "Name: " + name + "\n";
		s += "Clef: " + clef.toString() + "\n";
		s += "Key: " + keySignature.toString() + "\n";
		s += "Time: " + timeSignature.toString() + "\n";
		return s;
	}

	public String getName(){
		return name;
	}

	public TimeSignature getTimeSignature() {
		return timeSignature;
	}


	public Clef getClef() {
		return clef;
	}

	public int getStaves(){
		return numStaves;
	}

	public String getScoreCode(){
		return scoreCode;
	}

	public BufferedImage getScore(){
		return image;
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		System.out.println(e.getPoint());
		checkPoint(e.getPoint());
		
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}


	
}

	

